package us.wili.dev.netty.jwt;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import us.wili.dev.common.exception.BaseException;
import us.wili.dev.common.exception.BaseExceptionEnum;
import us.wili.dev.exception.ErrorCode;
import us.wili.dev.exception.UserException;
import us.wili.dev.jwt.model.ChannelEnum;
import us.wili.dev.jwt.model.Constants;
import us.wili.dev.jwt.model.IUser;
import us.wili.dev.jwt.session.redis.RedisOperationsSessionRepository;
import us.wili.dev.jwt.session.redis.RedisSession;
import us.wili.dev.jwt.session.web.http.HttpSessionStrategyHelper;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by JasonY on 18/8/31.
 */
@Resource
public class JwtSessionUtil {
    private static final Integer CAPTCHA_MAX_ERROR_TIMES = 5;
    private static final Long CAPTCHA_EXPIRATION = 900L; // 15分钟


    @Resource
    protected RedisOperationsSessionRepository redisOperationsSessionRepository;
    @Resource
    protected HttpSessionStrategyHelper httpSessionStrategyHelper;
    @Resource
    protected JwtUtil jwtUtil;
    @Value("${server.session.timeout:604800}")
    protected int maxInactiveInterval;

    public RedisSession getSession(HttpServletRequest request) {
        // 所有请求均经过AlwaysCreateJwtTokenFilter，request中已有session信息
        Object session = request.getAttribute(Constants.SESSION_KEY);
        if (session == null) {
            throw new UserException(BaseExceptionEnum.ANNOYMOUS);
        }

        RedisSession redisSession = (RedisSession) session;
        return  redisSession;
    }

    public RedisSession getAuthenticatedSession(HttpServletRequest request) {
        RedisSession session = getSession(request);
        if (session == null) {
            throw new UserException(BaseExceptionEnum.ANNOYMOUS);
        }
        JwtUser jwtUser = (JwtUser) session.getJwtUserInfo();

        if (jwtUser.getAnonymous()) {
            throw new UserException(BaseExceptionEnum.ANNOYMOUS);
        }
        return session;
    }

    public Long getUserIdFromAuthenticatedSession(HttpServletRequest request) {
        RedisSession session = getAuthenticatedSession(request);
        return ((JwtUser) session.getJwtUserInfo()).getUserId();
    }

//    public JwtUser getJwtUserInfo(User user, ChannelEnum channelEnum) {
//        JwtUser jwtUser = new JwtUser();
//
//        jwtUser.setAnonymous(false);
//        jwtUser.setChannel(channelEnum.getCode());
//
//        jwtUser.setUserId(user.getId());
//        jwtUser.setNickname(user.getPhone());
//        jwtUser.setPhone(user.getPhone());
//        jwtUser.setCertificationCategory(user.getCertCategory());
//
//        if (user.getWechatAccount() != null) {
//            jwtUser.setWxHeadimgurl(user.getWechatAccount().getHeadimgurl());
//            jwtUser.setWxNickname(user.getWechatAccount().getNickname());
//            jwtUser.setWxOpenid(user.getWechatAccount().getOpenid());
//        }
//
//        return jwtUser;
//    }

    /**
     * 将IUser对象序列化为Jwt Token，并根据token策略(cookie／header)写入response
     *
     * @param iUser 用户对象
     * @param request
     * @param response
     * @return
     */
    public RedisSession createSession(IUser iUser, HttpServletRequest request, HttpServletResponse response) {
        // 生成新token
        String token = jwtUtil.generateToken(iUser, maxInactiveInterval);

        // 复制原会话缓存中的数据，新建会话
        RedisSession oldSession = (RedisSession) request.getAttribute(Constants.SESSION_KEY);
        RedisSession newSession = oldSession == null ?
                new RedisSession(redisOperationsSessionRepository, token)
                :
                new RedisSession(redisOperationsSessionRepository, token, oldSession.getCached());
        // 保存会话
        redisOperationsSessionRepository.save(newSession);
        // 将token写入cookie或头部
        httpSessionStrategyHelper.onNewSession(newSession, request, response);

        return newSession;
    }

    /**
     * 清除会话记录
     *
     * @param request
     * @param response
     */
    public void clearSession(HttpServletRequest request, HttpServletResponse response) {
        RedisSession oldSession = (RedisSession) request.getAttribute(Constants.SESSION_KEY);

        if (oldSession != null) {
            redisOperationsSessionRepository.delete(oldSession.getToken());
            httpSessionStrategyHelper.onInvalidateSession(request, response);
        }
    }

    protected String getErrorTimesKey(String cacheKey) {
        return cacheKey + ":errorTimes";
    }

    public int getErrorTimes(RedisSession session, String cacheKey) {
        String errorTimesKey = getErrorTimesKey(cacheKey);
        Integer errorTimes = session.getAttribute(errorTimesKey);
        errorTimes = errorTimes == null ? 1 : errorTimes + 1;

        return errorTimes;
    }

    public int incrErrorTimes(RedisSession session, String cacheKey) {
        String errorTimesKey = getErrorTimesKey(cacheKey);
        int errorTimes = getErrorTimes(session, cacheKey);

        session.setAttribute(errorTimesKey, errorTimes);
        return errorTimes;
    }

    public void verifyCaptcha(RedisSession session, String cacheKey, String captcha) {
        String errorTimesKey = getErrorTimesKey(cacheKey);
        String cacheCaptcha = session.getAttribute(cacheKey);

        if (cacheCaptcha == null || !cacheCaptcha.equals(captcha)) {
            Integer errorTimes = getErrorTimes(session, cacheKey);

            if (errorTimes < CAPTCHA_MAX_ERROR_TIMES) {
                session.setAttribute(errorTimesKey, errorTimes);
                throw new BaseException(ErrorCode.CAPTCHA_ERROR.getMessage(), ErrorCode.CAPTCHA_ERROR.getCode());
            } else {
                session.removeAttribute(errorTimesKey);
                session.removeAttribute(cacheKey);
                throw new BaseException(ErrorCode.CAPTCHA_ERROR.getMessage(), ErrorCode.CAPTCHA_ERROR.getCode());
            }
        }

        // 删除验证码
        session.removeAttribute(errorTimesKey);
        session.removeAttribute(cacheKey);
    }

    public void cacheCaptcha(RedisSession session, String captchaKey, String captcha, Long expiration) {
        String errorTimesKey = captchaKey + ":errorTimes";

        session.removeAttribute(errorTimesKey);
        session.removeAttribute(captchaKey);

        session.setAttribute(captchaKey, captcha, expiration);
    }

    public void cacheCaptcha(RedisSession session, String captchaKey, String captcha) {
        cacheCaptcha(session, captchaKey, captcha, CAPTCHA_EXPIRATION);
    }
}
